package com.boilermaker.mq.mqserver.core;

import com.boilermaker.mq.common.MqException;

/*
 * * 转发规则实现（对 AMQP 协议的实现）：
 *
 * ----------> bindingKey：
 * 给绑定指定的字符串（相当于锁），支持数字、字母、下划线，使用 “.” 作为分隔符，使用 “*” 和 “#” 作为通配符，
 * “*” 和 “#” 都只能作为独立的部分存在，“*” 只能匹配一个独立的部分，“#” 可以匹配任意个（包括 0）独立的部分。
 * 合法的情况：aaa.bbb.ccc 或 aaa.*.ccc 或 aaa.#.ccc 或 aaa.*.*.ccc 或其他
 * 非法的情况：aaa.*bb.ccc 或 aaa.#bb.ccc 或 aaa.*.#.ccc 或 aaa.#.*.ccc 或 aaa.#.#.ccc 或其他
 *
 * ----------> routingKey：
 * 消息中指定的字符串（相当于钥匙），支持数字、字母、下划线，使用 “.” 作为分隔符。
 *
 * ----------> 匹配原则：
 * bindingKey 不设通配符的情况下，要求 bindingKey 和 routingKey 完全一致。
 * bindingKey 设有通配符的情况下：
 * 如 aaa.*.ccc，此时的 routingKey：
 * 合法的情况：aaa.bbb.ccc 或 aaa.b.ccc 或其他
 * 非法的情况：aaa.bbb.bbb.ccc 或其他
 * 如 aaa.#.ccc，此时的 routingKey：
 * 合法的情况：aaa.bbb.ccc 或 aaa.b.ccc 或 aaa.bbb.bbb.ccc 或其他
 * 非法的情况：aab.bbb.bbb.ccc 或 aaa.b.b 或其他
 */

public class Router {
    public boolean checkBindingKey(String bindingKey) {
        if (bindingKey.isEmpty()) return true;
        // 先检查单个字符的合法性
        for (int i = 0; i < bindingKey.length(); i++) {
            char ch = bindingKey.charAt(i);
            if (ch >= 'A' && ch <= 'Z') continue;
            if (ch >= 'a' && ch <= 'z') continue;
            if (ch >= '0' && ch <= '9') continue;
            if (ch == '_' || ch == '.' || ch == '*' || ch == '#') continue;
            // 能运行到这里说明该字符不是上面任何一种合法情况，则判定为非法
            return false;
        }
        // 再检查字符与 * 和 # 组合的合法性，使用 split 拆分 "."
        String[] words = bindingKey.split("\\.");
        // 确保 * 或 # 独立存在于一个 word
        for (String word : words) {
            if (word.length() > 1 && (word.contains("*") || word.contains("#"))) return false;
        }
        // 确保 * 或 # 不相邻（除 **）
        for (int i = 0; i < words.length - 1; i++) {
            if (words[i].equals("#") && words[i + 1].equals("#")) return false;
            if (words[i].equals("#") && words[i + 1].equals("*")) return false;
            if (words[i].equals("*") && words[i + 1].equals("#")) return false;
        }
        return true;
    }

    public boolean checkRoutingKey(String routingKey) {
        if (routingKey.isEmpty()) return true;
        // 遍历字符串，检查每个字符，看是否遇到非法情况
        for (int i = 0; i < routingKey.length(); i++) {
            char ch = routingKey.charAt(i);
            if (ch >= 'A' && ch <= 'Z') continue;
            if (ch >= 'a' && ch <= 'z') continue;
            if (ch >= '0' && ch <= '9') continue;
            if (ch == '_' || ch == '.') continue;
            // 能运行到这里说明该字符不是上面任何一种合法情况，则判定为非法
            return false;
        }
        return true;
    }

    public boolean route(ExchangeType exchangeType, Binding binding, Message message) throws MqException {
        if (exchangeType == ExchangeType.FANOUT) {
            return true;
        } else if (exchangeType == ExchangeType.TOPIC) {
            return routeTopic(binding, message);
        } else {
            throw new MqException("交换机类型非法, exchangeType=" + exchangeType);
        }
    }

    private boolean routeTopic(Binding binding, Message message) {
        String[] bindingKey = binding.getBindingKey().split("\\.");
        String[] routingKey = message.getRoutingKey().split("\\.");
        int bindingIndex = 0, routingIndex = 0;

        while (bindingIndex < bindingKey.length && routingIndex < routingKey.length) {
            if (bindingKey[bindingIndex].equals("*")) {
                // 遇到 “*” 则无需做任何比较
                bindingIndex++;
                routingIndex++;
            } else if (bindingKey[bindingIndex].equals("#")) {
                // 遇到 “#” 则看 bindingKey 的下一个位置，再尝试在 routingKey 中找这个值
                bindingIndex++;
                // 如果此时 bindingIndex 已经走到了末尾，证明 “#” 是数组最后一个元素，
                // 那么无论 routingKey 后面有什么元素都可以匹配成功
                if (bindingIndex == bindingKey.length) return true;
                // 如果 “#” 不是最后一个元素，则尝试在 routingKey 中找 “#” 后一位的值
                boolean find = false; // 设置标志位
                for (int i = routingIndex; i < routingKey.length; i++) {
                    // 命中
                    if (routingKey[i].equals(bindingKey[bindingIndex])) {
                        find = true; // 更改标志位
                        routingIndex = i;
                        break;
                    }
                }
                if (!find) return false; // 始终未命中
                // 命中则继续前进
                bindingIndex++;
                routingIndex++;
            } else { // 遇到一般字符串则要求二者完全相同
                if (!bindingKey[bindingIndex].equals(routingKey[routingIndex])) return false;
                bindingIndex++;
                routingIndex++;
            }
        }
        // 出循环后需确认两个指针都走到末尾
        return bindingIndex == bindingKey.length && routingIndex == routingKey.length;
    }
}
